<?php

namespace simpleHandle\Component\UtilConsul;

use DCarbone\PHPConsulAPI\Agent\AgentServiceRegistration;
use DCarbone\PHPConsulAPI\Config as ConsulConfig;
use DCarbone\PHPConsulAPI\Consul as ConsulObj;
use DCarbone\PHPConsulAPI\KV\KVPair;
use GuzzleHttp\Client as GuzzleHttpClient;
use simpleHandle\Exception\UtilException;
use Throwable;

class Consul
{
    public GuzzleHttpClient $client;
    public ConsulConfig     $config;
    public ConsulObj        $consul;
    public array            $consulConfig = [
        'HttpClient'         => '',
        'Address'            => '127.0.0.1:8500',
        'Scheme'             => 'http',
        'Datacenter'         => 'default',
        'HttpAuth'           => 'user:pass',
        'WaitTime'           => '5s',
        'Token'              => '',
        'InsecureSkipVerify' => true,
        'CAFile'             => '',
        'CertFile'           => '',
        'KeyFile'            => '',
        'JSONEncodeOpts'     => 0
    ];

    public function __construct(array $consulConfig)
    {
        $this->consulConfig['HttpClient'] = new GuzzleHttpClient();
        $this->consulConfig               = array_merge($this->consulConfig, $consulConfig);
        $config                           = new ConsulConfig($this->consulConfig);
        $this->consul                     = new ConsulObj($config);
    }

    /**
     * 服务注册
     * @throws UtilException
     */
    public function ServiceRegister($name, $id, $add, $port, array $check = [], array $tags = []): bool
    {
        try {
            $config = [
                'Name'    => $name,
                'ID'      => $id,
                'Tags'    => $tags,
                'Address' => $add,
                'Port'    => $port,
            ];
            if (empty($check)) {
                $config['Check'] = [
                    'Name'                           => "Check_$name",
                    'CheckID'                        => $id,
                    'Interval'                       => '3s',
                    'DeregisterCriticalServiceAfter' => '1m',
                    'TCP'                            => "$add:$port",
                    'Timeout'                        => '1s',
                    'FailuresBeforeWarning'          => 3,
                    'FailuresBeforeCritical'         => 3,
                ];
            } else {
                $config['Check'] = $check;
            }
            $AgentServiceRegistration = new AgentServiceRegistration($config);
            $agent                    = $this->consul->Agent();
            $res                      = $agent->ServiceRegister($AgentServiceRegistration);
            if (!empty($res)) {
                return false;
            }
            return true;
        } catch (Throwable $th) {
            throw new UtilException($th->getMessage(), UtilException::Consul_ERROR_CODE);
        }
    }

    /**
     * 服务发现-获取一个健康的服务节点
     * @throws UtilException
     */
    public function GetOneHealthService(string $name): array
    {
        try {
            $res    = $this->consul->Health->Service($name);
            $resArr = json_decode(json_encode($res->getValue()), true);
            $arr    = [];

            foreach ($resArr as $v) {
                $arr[] = [
                    'address' => $v['Service']['Address'],
                    'port'    => $v['Service']['Port']
                ];
            }
            return $arr[array_rand($arr, 1)];
        } catch (Throwable $th) {
            throw new UtilException($th->getMessage(), UtilException::Consul_ERROR_CODE);
        }
    }

    /**
     * 服务发现-获取所有服务
     * @throws UtilException
     */
    public function GetAllHealthService(string $name, bool $allData = false, $passing = true): array
    {
        try {
            $res    = $this->consul->Health->Service($name, '', $passing);
            $resArr = json_decode(json_encode($res->getValue()), true);
            $arr    = [];
            if ($allData) {
                return $arr;
            }
            foreach ($resArr as $v) {
                $arr[] = $v['Service']['Address'] . ':' . $v['Service']['Port'];
            }
            return $arr;
        } catch (Throwable $th) {
            throw new UtilException($th->getMessage(), UtilException::Consul_ERROR_CODE);
        }
    }

    /**
     * @throws UtilException
     */
    public function GetKV(string $key): object
    {
        try {
            $res = $this->consul->KV->Get($key)->getValue()->Value;
            return json_decode($res);
        } catch (Throwable $th) {
            throw new UtilException($th->getMessage(), UtilException::Consul_ERROR_CODE);
        }
    }

    /**
     * @throws UtilException
     */
    public function SetKV(string $key, array $value): bool
    {
        try {
            $kvpair = new kvpair();
            $kvpair->setKey($key);
            $kvpair->setValue(json_encode($value, JSON_THROW_ON_ERROR));
            $wres = $this->consul->KV->Put($kvpair);
            if (!empty($wres->getErr())) {
                return false;
            }
            return true;
        } catch (Throwable $th) {
            throw new UtilException($th->getMessage(), UtilException::Consul_ERROR_CODE);
        }
    }
}